package prefuse.data.io;

import java.io.BufferedOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;

import prefuse.data.Table;
import prefuse.util.TypeLib;
import prefuse.util.collections.IntIterator;

/**
 * TableWriter for fixed-width text files, that encode one row of table
 * data per line use a fixed number of characters for each data column.
 * Writing such tables requires use of a schema description that describes
 * the fixed-widths for each individual column.
 * The {@link prefuse.data.io.FixedWidthTextTableSchema} class provides
 * this functionality. A schema description must be written separately into
 * a different file.
 * 
 * @author <a href="http://jheer.org">jeffrey heer</a>
 */
public class FixedWidthTextTableWriter extends AbstractTableWriter {

    // the schema description
    private FixedWidthTextTableSchema m_schema;

    /**
     * Creates a new FixedWidthTextTableWriter using the given schema.
     * @param schema the schema description of the fixed-width text column lengths
     */
    public FixedWidthTextTableWriter(FixedWidthTextTableSchema schema) {
        m_schema = schema;
    }

    /**
     * Creates a new FixedWidthTextTableWriter using the schema at
     * the given location.
     * @param location a location string (filename, URL, or resource 
     * locator) for the schema description of the fixed-width text column lengths
     * @throws DataIOException if an IO exception occurs while loading the schema
     */
    public FixedWidthTextTableWriter(String location) throws DataIOException {
        this(FixedWidthTextTableSchema.load(location));
    }

    // ------------------------------------------------------------------------    

    /**
     * Get the schema description describing the data columns' fixed widths
     * @return the fixed-width table schema description
     */
    public FixedWidthTextTableSchema getFixedWidthSchema() {
        return m_schema;
    }

    /**
     * Set the schema description describing the data columns' fixed widths
     * @param schema the fixed-width table schema description
     */
    public void setFixedWidthSchema(FixedWidthTextTableSchema schema) {
        m_schema = schema;
    }

    // ------------------------------------------------------------------------

    /**
     * @see prefuse.data.io.TableWriter#writeTable(prefuse.data.Table, java.io.OutputStream)
     */
    public void writeTable(Table table, OutputStream os) throws DataIOException {
        try {
            // get print stream
            PrintStream out = new PrintStream(new BufferedOutputStream(os));

            // build array of column padding
            char[] pad = new char[table.getColumnCount()];
            boolean[] pre = new boolean[table.getColumnCount()];
            for (int i = 0; i < table.getColumnCount(); ++i) {
                Class type = table.getColumnType(i);
                pre[i] = TypeLib.isNumericType(type);
                pad[i] = pre[i] ? '0' : ' ';
            }

            // write out data
            for (IntIterator rows = table.rows(); rows.hasNext();) {
                int row = rows.nextInt();
                for (int i = 0; i < table.getColumnCount(); ++i) {
                    out.print(pack(table.getString(row, i), m_schema.getColumnLength(i), pre[i], pad[i]));
                }
                out.println();
            }

            // finish up
            out.flush();
        } catch (Exception e) {
            throw new DataIOException(e);
        }
    }

    /**
     * Pads or truncates a string as necessary to fit within the column length.
     */
    private static String pack(String value, int len, boolean prepend, char pad) {
        int vlen = value.length();
        if (vlen < len) {
            StringBuffer sbuf = new StringBuffer();
            if (prepend)
                sbuf.append(value);
            for (int i = len; i < vlen; ++i)
                sbuf.append(pad);
            if (!prepend)
                sbuf.append(value);
            return sbuf.toString();
        } else {
            return value.substring(0, len);
        }
    }

} // end of class FixedWidthTextTableWriter
